#include <jni.h>
#include <string>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <android/log.h>
#include "kvstore.h"
#include <android/log.h>

extern "C"
JNIEXPORT void JNICALL
Java_com_cj_kvstore_KVStore_jniInitialize(JNIEnv *env, jclass clazz, jstring root_dir) {
    const char * rootDir = env->GetStringUTFChars(root_dir, nullptr);
    if(rootDir){
        KVStore::initializeKVStore(rootDir);
        env->ReleaseStringUTFChars(root_dir,rootDir);
    }
}

extern "C"
JNIEXPORT jlong JNICALL
Java_com_cj_kvstore_KVStore_getDefaultKVStore(JNIEnv *env, jclass clazz) {
    KVStore* kv = KVStore::defaultKVStore();
    return reinterpret_cast<jlong>(kv);
}

extern "C"
JNIEXPORT jlong JNICALL
Java_com_cj_kvstore_KVStore_getKVStoreWithID(JNIEnv *env, jclass clazz, jstring name) {
    const char * _id = env->GetStringUTFChars(name, nullptr);
    if(_id){
        KVStore* kv = KVStore::kvStoreWithID(_id);
        env->ReleaseStringUTFChars(name,_id);
        return reinterpret_cast<jlong>(kv);
    }
    return 0;
}

extern "C"
JNIEXPORT void JNICALL
Java_com_cj_kvstore_KVStore_putInt(JNIEnv *env, jobject thiz, jlong handle,jstring key,jint value) {
    const char *_key = env->GetStringUTFChars(key, nullptr);
    if(_key){
        KVStore *kv = reinterpret_cast<KVStore *>(handle);
        kv->putInt(_key,value);
        env->ReleaseStringUTFChars(key,_key);
    }
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_cj_kvstore_KVStore_getInt(JNIEnv *env, jobject thiz, jlong handle, jstring key,
                                   jint default_value) {
    const char *_key = env->GetStringUTFChars(key, nullptr);
    if(_key){
        KVStore *kv = reinterpret_cast<KVStore *>(handle);
        int storeValue = kv->getInt(_key,default_value);
        env->ReleaseStringUTFChars(key,_key);
        return storeValue;
    }
    return default_value;
}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_cj_kvstore_KVStore_getString(JNIEnv *env, jobject thiz, jlong handle, jstring key) {
    const char *_key = env->GetStringUTFChars(key, nullptr);
    if(_key){
        KVStore *kv = reinterpret_cast<KVStore *>(handle);
        const char* storeValue = kv->getString(_key);
        if(storeValue){
            env->ReleaseStringUTFChars(key,_key);
            jstring resultString = env->NewStringUTF(storeValue);
            env->ReleaseStringUTFChars(resultString,storeValue);
            return resultString;
        }
    }
    return nullptr;
}

extern "C"
JNIEXPORT void JNICALL
Java_com_cj_kvstore_KVStore_putString(JNIEnv *env, jobject thiz, jlong handle, jstring key,
                                      jstring value) {
    const char *_key = env->GetStringUTFChars(key, nullptr);
    if(_key){
        const char *_value = value != nullptr ? env->GetStringUTFChars(value, nullptr) : nullptr;
        KVStore *kv = reinterpret_cast<KVStore *>(handle);
        kv->putString(_key,_value);
        env->ReleaseStringUTFChars(key,_key);
        if(_value)env->ReleaseStringUTFChars(value,_value);
    }
}

extern "C"
JNIEXPORT void JNICALL
Java_com_cj_kvstore_KVStore_putDouble(JNIEnv *env, jobject thiz, jlong handle, jstring key,
                                      jdouble value) {
    const char *_key = env->GetStringUTFChars(key, nullptr);
    if(_key){
        KVStore *kv = reinterpret_cast<KVStore *>(handle);
        kv->putDouble(_key,value);
        env->ReleaseStringUTFChars(key,_key);
    }
}

extern "C"
JNIEXPORT jdouble JNICALL
Java_com_cj_kvstore_KVStore_getDouble(JNIEnv *env, jobject thiz, jlong handle, jstring key,
                                      jdouble default_value) {
    const char *_key = env->GetStringUTFChars(key, nullptr);
    if(_key){
        KVStore *kv = reinterpret_cast<KVStore *>(handle);
        double_t storeValue = kv->getDouble(_key,default_value);
        env->ReleaseStringUTFChars(key,_key);
        return storeValue;
    }
    return default_value;
}

extern "C"
JNIEXPORT void JNICALL
Java_com_cj_kvstore_KVStore_putBoolean(JNIEnv *env, jobject thiz, jlong handle, jstring key,
                                       jboolean value) {
    const char *_key = env->GetStringUTFChars(key, nullptr);
    if(_key){
        KVStore *kv = reinterpret_cast<KVStore *>(handle);
        kv->putBoolean(_key,value);
        env->ReleaseStringUTFChars(key,_key);
    }
}

extern "C"
JNIEXPORT jboolean JNICALL
Java_com_cj_kvstore_KVStore_getBoolean(JNIEnv *env, jobject thiz, jlong handle, jstring key,
                                       jboolean default_value) {
    const char *_key = env->GetStringUTFChars(key, nullptr);
    if(_key){
        KVStore *kv = reinterpret_cast<KVStore *>(handle);
        double_t storeValue = kv->getBoolean(_key,default_value);
        env->ReleaseStringUTFChars(key,_key);
        return storeValue;
    }
    return default_value;
}

extern "C"
JNIEXPORT void JNICALL
Java_com_cj_kvstore_KVStore_removeValueFromKey(JNIEnv *env, jobject thiz, jlong handle,
                                               jstring key) {
    const char *_key = env->GetStringUTFChars(key, nullptr);
    if(_key){
        KVStore *kv = reinterpret_cast<KVStore *>(handle);
        kv->removeValueFromKey(_key);
        env->ReleaseStringUTFChars(key,_key);
    }
}

extern "C"
JNIEXPORT jboolean JNICALL
Java_com_cj_kvstore_KVStore_containsKey(JNIEnv *env, jobject thiz, jlong handle, jstring key) {
    const char *_key = env->GetStringUTFChars(key, nullptr);
    if(_key){
        KVStore *kv = reinterpret_cast<KVStore *>(handle);
        kv->containsKey(_key);
        env->ReleaseStringUTFChars(key,_key);
    }
}

extern "C"
JNIEXPORT void JNICALL
Java_com_cj_kvstore_KVStore_putBytes(JNIEnv *env, jobject thiz, jlong handle, jstring key,
                                     jbyteArray value) {
    KVStore *kv = reinterpret_cast<KVStore *>(handle);
    if(key && kv){
        const char *_key = env->GetStringUTFChars(key, nullptr);
        if(value){
            jsize  len = env->GetArrayLength(value);
            void *bufferPtr = env->GetPrimitiveArrayCritical(value, nullptr);
            if(bufferPtr){
                kv->putValue(_key,bufferPtr,len);
                env->ReleasePrimitiveArrayCritical(value,bufferPtr,JNI_ABORT);
            }else{
                kv->putValue(_key,bufferPtr,0);
                //__android_log_print(ANDROID_LOG_ERROR,"KVStore","fail to get array: %s=%p", _key, value);
            }
        }else{
            kv->removeValueFromKey(_key);
        }
    }
}

extern "C"
JNIEXPORT jbyteArray JNICALL
Java_com_cj_kvstore_KVStore_getBytes(JNIEnv *env, jobject thiz, jlong handle, jstring key) {
    KVStore *kv = reinterpret_cast<KVStore *>(handle);
    if(key && kv){
        const char *_key = env->GetStringUTFChars(key, nullptr);
        ProtoBuf* buf = kv->getValueProtoBuf(_key);
        if(buf){
            int len = buf->length();
            buf->restore();
            void* _value = buf->readValue();
            if(len > 0){
                jbyteArray result = env->NewByteArray(len);
                env->SetByteArrayRegion(result,0,len,(const jbyte *)_value);
                free(_value);
                return result;
            }
        }
    }
    return nullptr;
}

extern "C"
JNIEXPORT void JNICALL
Java_com_cj_kvstore_KVStore_clear(JNIEnv *env, jobject thiz, jlong handle) {
    KVStore *kv = reinterpret_cast<KVStore *>(handle);
    if(kv){
        kv->clear();
    }
}



